// ==UserScript==
// @name           grill-sdk
// @namespace      org.codegrill.ikariam
// @description    Displays spy defense information of a colony
//
//                 Author: brains
//                 Server: Theta
//                 Email: bwdenton@gmail.com
//                 Copyright 2009 Brian Denton
//
//                 This program is free software: you can redistribute it and/or modify
//                 it under the terms of the GNU General Public License as published by
//                 the Free Software Foundation, either version 3 of the License, or
//                 (at your option) any later version.
//
//                 This program is distributed in the hope that it will be useful,
//                 but WITHOUT ANY WARRANTY; without even the implied warranty of
//                 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//                 GNU General Public License for more details <http://www.gnu.org/licenses/>.
//
// @include        http://*.ikariam.*
// @exclude        http://board.ikariam.*
// ==/UserScript==

////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////
const TOTAL_BUILDINGS = 15;

//Class/type names for buildings
const BUILDING_TYPE_TOWN_HALL = "townHall";
const BUILDING_TYPE_TRADING_PORT = "port";
const BUILDING_TYPE_TRADING_POST = "branchOffice";
const BUILDING_TYPE_HIDEOUT = "safehouse";
const BUILDING_TYPE_SHIPYARD = "shipyard";
const BUILDING_TYPE_PALACE = "palace";

const CLASS_SCORE = "score";
const CLASS_PLACE = "place";
const CLASS_ALLY = "allytag";
const CLASS_PLAYER = "name"

//URL values
const URL_VIEW = "view";
const URL_ID = "id";
const URL_FUNCTION = "function";
const URL_FUNCTION_LOGIN = "login";
const URL_REQUEST_HIGHSCORE = "score";
const URL_REQUEST_GENERALS = "army_score_main";
const URL_REQUEST_GOLD = "trader_score_secondary";

const VIEW_COLONY = "city";
const VIEW_ISLAND = "island";
const VIEW_WORLD = "worldmap_iso";

//Cache key values
const CACHE_LAST_PLAYER = "last.player";
const CACHE_ISLAND = "island";
const CACHE_COLONY = "colony";
const CACHE_LEVEL = "level";
const CACHE_TYPE = "type";
const CACHE_NAME = "name";

const CACHE_SPIES = "spies";
const CACHE_TRAINED_SPIES = "spies.trained";

const CACHE_POPULATION = "population";
const CACHE_POPULATION_CAP = "population.cap";
const CACHE_POPULATION_GROWTH = "population.growth";

const CACHE_WOOD = "wood";
const CACHE_WOOD_CAP = "wood.cap";
const CACHE_WOOD_GROWTH = "wood.growth";

const CACHE_WINE = "wine";
const CACHE_WINE_CAP = "wine.cap";
const CACHE_WINE_GROWTH = "wine.growth";

const CACHE_MARBLE = "marble";
const CACHE_MARBLE_CAP = "marble.cap";
const CACHE_MARBLE_GROWTH = "marble.growth";

const CACHE_CRYSTAL = "crystal";
const CACHE_CRYSTAL_CAP = "crystal.cap";
const CACHE_CRYSTAL_GROWTH = "crystal.growth";

const CACHE_SULFUR = "sulfur";
const CACHE_SULFUR_CAP = "sulfur.cap";
const CACHE_SULFUR_GROWTH = "sulfur.growth";


////////////////////////////////////////////////////////////////////////////////
// Override/Add Javascript Methods
////////////////////////////////////////////////////////////////////////////////
Array.prototype.contains = function (value) {
    for each (var i in this) {
        if (i == value) { return true; }
    }

    return false;
};

////////////////////////////////////////////////////////////////////////////////
// Initialization Methods
////////////////////////////////////////////////////////////////////////////////
function grill_init() {
    grill_init_player();
    grill_init_colony();
}

function grill_init_player() {
    if (grill_viewingMyColony()) {
        grill_cache_setPlayer(grill_getServer(), grill_getPlayer(null));
    }
}

function grill_init_colony() {
    var server = grill_getServer();
    var player = grill_getPlayer(grill_cache_getPlayer(server));
    var id = grill_getColonyId();

    grill_cache_setColonyLevel(server, player, id, grill_getColonyLevel(null));

    if (grill_viewingMyColony()) { //We're looking at our own colony
        //Population
        grill_cache_setPopulation(server, player, id, grill_getPopulation(null));
        grill_cache_setPopulationCap(server, player, id, grill_getPopulationCap(null));
        grill_cache_setPopulationGrowth(server, player, id, grill_getPopulationGrowth(null));

        //Spies
        grill_cache_setSpies(server, player, id, grill_getSpies(null));
        grill_cache_setTrainedSpies(server, player, id, grill_getTrainedSpies(null));

        //Resources
        grill_cache_setWood(server, player, id, grill_getWood(null));
        grill_cache_setWoodCap(server, player, id, grill_getWoodCap(null));
        grill_cache_setWoodGrowth(server, player, id, grill_getWoodGrowth(null));

        grill_cache_setWine(server, player, id, grill_getWine(null));
        grill_cache_setWineCap(server, player, id, grill_getWineCap(null));
        grill_cache_setWineGrowth(server, player, id, grill_getWineGrowth(null));

        grill_cache_setMarble(server, player, id, grill_getMarble(null));
        grill_cache_setMarbleCap(server, player, id, grill_getMarbleCap(null));
        grill_cache_setMarbleGrowth(server, player, id, grill_getMarbleGrowth(null));

        grill_cache_setCrystal(server, player, id, grill_getCrystal(null));
        grill_cache_setCrystalCap(server, player, id, grill_getCrystalCap(null));
        grill_cache_setCrystalGrowth(server, player, id, grill_getCrystalGrowth(null));

        grill_cache_setSulfur(server, player, id, grill_getSulfur(null));
        grill_cache_setSulfurCap(server, player, id, grill_getSulfurCap(null));
        grill_cache_setSulfurGrowth(server, player, id, grill_getSulfurGrowth(null));
    }

    if (grill_getView() == VIEW_COLONY) {
        //Buildings
        var buildings = grill_getCurrentBuildings();
        for each (var b in buildings) {
            grill_cache_setBuildingName(server, player, id, b.position, b.name);
            grill_cache_setBuildingType(server, player, id, b.position, b.type);
            grill_cache_setBuildingLevel(server, player, id, b.position, b.level);
        }
    }
}


////////////////////////////////////////////////////////////////////////////////
// Ikariam Classes
////////////////////////////////////////////////////////////////////////////////

/**
 *  Definition of the Colony class
 */
function Grill_Colony(server, player, id) {
    this.server = server;
    this.player = player;
    this.island = null;
    this.id = id;
    this.level = 0;

    this.spies = 0;
    this.trainedSpies = 0;

    this.population = new Array();
    this.population['amount'] = 0;
    this.population['cap'] = 0;
    this.population['growth'] = 0.0;

    this.wood = new Array();
    this.wood['amount'] = 0;
    this.wood['cap'] = 0;
    this.wood['growth'] = 0.0;

    this.wine = new Array();
    this.wine['amount'] = 0;
    this.wine['cap'] = 0;
    this.wine['growth'] = 0.0;

    this.marble = new Array();
    this.marble['amount'] = 0;
    this.marble['cap'] = 0;
    this.marble['growth'] = 0.0;

    this.crystal = new Array();
    this.crystal['amount'] = 0;
    this.crystal['cap'] = 0;
    this.crystal['growth'] = 0.0;

    this.sulfur = new Array();
    this.sulfur['amount'] = 0;
    this.sulfur['cap'] = 0;
    this.sulfur['growth'] = 0.0;

    this.buildings = new Array();
    this.units = new Array();
    this.ships = new Array();
}

/**
 *  Definition of the Building class
 */
function Grill_Building(name, type, level, position) {
    this.name = name;
    this.type = type;
    this.level = level;
    this.position = position;

    this.equals = function (object) {
        if (!object || this.name != object.name || this.type != object.type ||
            this.level != object.level || this.position != object.position) {
            return false;
        }

        return true;
    };
}

////////////////////////////////////////////////////////////////////////////////
// General Ikariam Functions
////////////////////////////////////////////////////////////////////////////////
function grill_getCurrentColony() {
    var server = grill_getServer();
    var player = grill_getPlayer(grill_cache_getPlayer(server));
    var id = grill_getColonyId();

    return grill_getColony(server, player, id);
}

/**
 * Get all information about the currently viewed colony
 */
function grill_getColony(server, player, id) {
    var colony = new Grill_Colony(server, player, id);

    colony.level = grill_getColonyLevel(grill_cache_getColonyLevel(server, player, id));
    colony.population = grill_getPopulation(grill_cache_getPopulation(server, player, id));
    colony.popcap = grill_getPopulationCap(grill_cache_getPopulationCap(server, player, id));
    colony.popgrowth = grill_getPopulationGrowth(grill_cache_getPopulationGrowth(server, player, id));

    //Resources
    colony.wood['amount'] = grill_getWood(grill_cache_getWood(server, player, id));
    colony.wood['cap'] = grill_getWoodCap(grill_cache_getWoodCap(server, player, id));
    colony.wood['growth'] = grill_getWoodGrowth(grill_cache_getWoodGrowth(server, player, id));

    colony.wine['amount'] = grill_getWine(grill_cache_getWine(server, player, id));
    colony.wine['cap'] = grill_getWineCap(grill_cache_getWineCap(server, player, id));
    colony.wine['growth'] = grill_getWineGrowth(grill_cache_getWineGrowth(server, player, id));

    colony.marble['amount'] = grill_getMarble(grill_cache_getMarble(server, player, id));
    colony.marble['cap'] = grill_getMarbleCap(grill_cache_getMarbleCap(server, player, id));
    colony.marble['growth'] = grill_getMarbleGrowth(grill_cache_getMarbleGrowth(server, player, id));

    colony.crystal['amount'] = grill_getCrystal(grill_cache_getCrystal(server, player, id));
    colony.crystal['cap'] = grill_getCrystalCap(grill_cache_getCrystalCap(server, player, id));
    colony.crystal['growth'] = grill_getCrystalGrowth(grill_cache_getCrystalGrowth(server, player, id));

    colony.sulfur['amount'] = grill_getSulfur(grill_cache_getSulfur(server, player, id));
    colony.sulfur['cap'] = grill_getSulfurCap(grill_cache_getSulfurCap(server, player, id));
    colony.sulfur['growth'] = grill_getSulfurGrowth(grill_cache_getSulfurGrowth(server, player, id));

    //Get all buildings and fill any missing building information with cached data
    for (var i = 0; i < TOTAL_BUILDINGS; i++) {
        var building = grill_getCurrentBuilding(i);

        if (building) {
            colony.buildings[i] = building;
        }

        if (colony.buildings[i]) {
            if (!colony.buildings[i].type) {
                colony.buildings[i].type = grill_cache_getBuildingType(server, player, id, i);
                colony.buildings[colony.buildings[i].type] = colony.buildings[i];
            }
            if (!(colony.buildings[i].level || colony.buildings[i].level == 0)) {
                colony.buildings[i].level = grill_cache_getBuildingLevel(server, player, id, i);
            }
            if (!colony.buildings[i].name) {
                colony.buildings[i].name = grill_cache_getBuildingName(server, player, id, i);
            }
            if (!(colony.buildings[i].position || colony.buildings[i].position == 0)) {
                colony.buildings[i].position = i;
            }
        }
        else {
            var name = grill_cache_getBuildingName(server, player, id, i);
            var type = grill_cache_getBuildingType(server, player, id, i);
            var level = grill_cache_getBuildingLevel(server, player, id, i);

            if (name && type && (level || level == 0)) {
                colony.buildings[i] = new Grill_Building(name, type, level, i);
                colony.buildings[type] = colony.buildings[i];
            }
        }
    }

    for (var i = 0; i < TOTAL_BUILDINGS; i++) {
        var building = grill_getCurrentBuilding(i);
        
        if (!building || !building.name) {
            
        }
        var name = grill_cache_getBuildingName(server, player, id, i);
        var type = grill_cache_getBuildingType(server, player, id, i);
        var level = grill_cache_getBuildingLevel(server, player, id, i);

            if (name && type && (level || level == 0)) {
                colony.buildings[i] = new Grill_Building(name, type, level, i);
                colony.buildings[type] = colony.buildings[i];
            }
    }

    return colony;
}

function grill_getCurrentBuildings() {
    var buildings = new Array();

    if (document.getElementById("locations")) {
        for (var i=0; i < TOTAL_BUILDINGS; i++) {
            buildings[i] = grill_getCurrentBuilding(i);
        }
    }

    return buildings;
}

function grill_getCurrentBuilding(position) {
    var type, name, level;
    var string, split;
    var building = null;

    var node = grill_xpath_getFirstNode("//li[@id='position" + position + "']");

    //Get the class name from the top node
    if (node && node.hasAttributes()) {
        node = node.attributes.getNamedItem("class");
        if (node) {
            type = node.nodeValue;
        }

        //Get the name and level of the building
        string = grill_xpath_getFirstNodesLastString("//li[@id='position" + position + "']/a/span");
        if (string) {
            //Get the level
            level = parseInt(string.replace(/^\D+/g, '')); //Remove everything but digits at the end of the string
    
            //Get the building name by removing the ending 'Level 24'
            split = string.split(/\s+/);
    
            var regex = new RegExp("\\s+" + split[split.length-2] + "\\s+" + split[split.length-1] + "\\s*$");
            name = string.replace(regex, '');
        }

        building = new Grill_Building(name, type, level, position);
    }

    return building;
}

/**
 * Get the server name from the current URL
 */
function grill_getServer() {
    return top.location.hostname;
}

function grill_getView() {
    var body = document.getElementsByTagName("body");
    var id = null;

    if (body && body[0]) {
        var attribute = body[0].attributes.getNamedItem("id");
        if (attribute && attribute.value) {
            id = attribute.value;
        }
    }

    return id;
}

/**
 * Get the player name currently in view
 */
function grill_getPlayer(defaultValue) {
    var xpath = "//div[@id='information']/div[@class='content']/ul[@class='cityinfo']/li[@class='owner']";
    var name = grill_xpath_getFirstNodesLastString(xpath, "Unable to get the players name from xpath");

    if (!name) {
        if (!defaultValue) {
            if (debug) { GM_log("grill_getPlayer(): unable to get the players name"); }
        }
        else {
            name = "" + defaultValue; //make sure it's a string
        }
    }

    return name;
}

/**
 * Get the island ID currently in view
 */
function grill_getIslandId() {
    var xpath = "//form[@id='changeCityForm']/ul/li[@class='viewIsland']/a";

    return grill_xpath_getFirstNodesUrlVariable(xpath, "id", "Unable to get the island ID");
}

/**
 * Get the colony ID currently in view
 */
function grill_getColonyId() {
    var xpath = "//form[@id='changeCityForm']/ul/li[@class='viewCity']/a";

    return grill_xpath_getFirstNodesUrlVariable(xpath, "id", "Unable to get the colony ID");
}

function grill_getActiveColonyId() {
    var xpath = "//select[@id='citySelect']/option[@selected='selected']";
    var node = grill_xpath_getFirstNode(xpath, "Unable to get the active colony id");
    if (node && node.attributes) {
        node = node.attributes.getNamedItem('value');
        if (node) {
            return node.nodeValue;
        }
    }

    return null;
}

function grill_viewingMyColony() {
    if (grill_getView() == VIEW_COLONY) {
        var id = grill_url_getVariable(URL_ID, document.location); 
        if (id != grill_getSelectedColonyId()) {
            return false;
        }
    }

    return true;
}

function grill_getSelectedColonyId() {
    var xpath = "//select[@id='citySelect']//option[@selected='selected']";
    var selected = grill_xpath_getFirstNode(xpath, "Unable to get the selected colony");
    var id = null;

    if (selected) {
        id = Number(selected.attributes.getNamedItem("value").nodeValue);
    }

    return id;
}

/**
 * Get the level of the town hall currently in view
 */
function grill_getColonyLevel(defaultValue) {
    var xpath = "//div[@id='information']/div[@class='content']/ul[@class='cityinfo']/li[@class='citylevel']";
    var level = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the level of the colony");

    if (!(level || level == 0)) {
        if (!(defaultValue || defaultValue == 0)) {
            if (debug) { GM_log("grill_getColonyLevel(): unable to get the colony level"); }
        }
        else if (Number(defaultValue)) {
            level = Number(defaultValue);
        }
        else {
            GM_log("grill_getColonyLevel(): the default value is not a number");
        }
    }

    return level;
}

function grill_getSpies(defaultValue) {
    var spies = null;
    var element = document.getElementById("spyCount");
    if (element) {
        spies = Number(element.value);
    }

    if (!(spies || spies == 0) && defaultValue) { spies = Number(defaultValue); }

    return spies;
}

function grill_getTrainedSpies(defaultValue) {
    var xpath = "//div[@id='mainview']/div[@class='contentBox01h']/div[@class='content']/p/span[@class='summary']/span[@title='Trained spies']";

    var spies = grill_xpath_getFirstNodesFirstInt(xpath, "Unable to get the trained spies of the colony");

    if (!(spies || spies == 0) && defaultValue) { spies = defaultValue; }

    return spies;
}

// Population //////////////////////////////////////////////////////////////////

/**
 * Get the population of the colony currently in view
 */
function grill_getPopulation() {
    var xpath = "//div[@id='CityOverview']/div[@class='content']/ul[@class='stats']/li[@class='space']/span[@class='value occupied']";
 
    return grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the population of the colony");
}

/**
 * Get the population cap of the colony currently in view
 */
function grill_getPopulationCap() {
    var xpath = "//div[@id='CityOverview']/div[@class='content']/ul[@class='stats']/li[@class='space']/span[@class='value total']";

    return grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the population cap of the colony");
}

/**
 * Get the population of the colony currently in view
 */
function grill_getPopulationGrowth() {
    var xpath = "//div[@id='CityOverview']/div[@class='content']/ul[@class='stats']/li[@class='growth growth_positive']/span[@class='value']";

    return grill_xpath_getFirstNodesLastFloat(xpath, "Unable to get the population growth of the colony");
}

// Wood ////////////////////////////////////////////////////////////////////////

function grill_getWood(defaultValue) {
    return grill_dom_getElementNumberById(document, "value_wood", defaultValue);
}

function grill_getWoodCap(defaultValue) {
    var xpath = "//div[@id='cityResources']/ul[@class='resources']/li[@class='wood']/div[@class='tooltip']";
    var cap = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the wood cap of the colony");

    if (!(cap || cap == 0) && defaultValue) { cap = defaultValue; }

    return cap;
}

function grill_getWoodGrowth(defaultValue) {
    return 0.0;
}

// Wine ////////////////////////////////////////////////////////////////////////

function grill_getWine(defaultValue) {
    return grill_dom_getElementNumberById(document, "value_wine", defaultValue);
}

function grill_getWineCap(defaultValue) {
    var xpath = "//div[@id='cityResources']/ul[@class='resources']/li[@class='wine']/div[@class='tooltip']";
    var cap = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the wine cap of the colony");

    if (!(cap || cap == 0) && defaultValue) { cap = defaultValue; }

    return cap;
}

function grill_getWineGrowth(defaultValue) {
    return 0.0;
}

// Marble //////////////////////////////////////////////////////////////////////

function grill_getMarble(defaultValue) {
    return grill_dom_getElementNumberById(document, "value_marble", defaultValue);
}

function grill_getMarbleCap(defaultValue) {
    var xpath = "//div[@id='cityResources']/ul[@class='resources']/li[@class='marble']/div[@class='tooltip']";
    var cap = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the marble cap of the colony");

    if (!(cap || cap == 0) && defaultValue) { cap = defaultValue; }

    return cap;
}

function grill_getMarbleGrowth(defaultValue) {
    return 0.0;
}

// Crystal /////////////////////////////////////////////////////////////////////

function grill_getCrystal(defaultValue) {
    return grill_dom_getElementNumberById(document, "value_crystal", defaultValue);
}

function grill_getCrystalCap(defaultValue) {
    var xpath = "//div[@id='cityResources']/ul[@class='resources']/li[@class='glass']/div[@class='tooltip']";
    var cap = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the crystal cap of the colony");

    if (!(cap || cap == 0) && defaultValue) { cap = defaultValue; }

    return cap;
}

function grill_getCrystalGrowth(defaultValue) {
    return 0.0;
}

// Sulfur //////////////////////////////////////////////////////////////////////

function grill_getSulfur(defaultValue) {
    return grill_dom_getElementNumberById(document, "value_sulfur", defaultValue);
}

function grill_getSulfurCap(defaultValue) {
    var xpath = "//div[@id='cityResources']/ul[@class='resources']/li[@class='sulfur']/div[@class='tooltip']";
    var cap = grill_xpath_getFirstNodesLastInt(xpath, "Unable to get the sulfur cap of the colony");

    if (!(cap || cap == 0) && defaultValue) { cap = defaultValue; }

    return cap;
}

function grill_getSulfurGrowth(defaultValue) {
    return 0.0;
}

////////////////////////////////////////////////////////////////////////////////
// Ikariam Cache Functions
////////////////////////////////////////////////////////////////////////////////
function grill_cache_getPlayer(server) {
    var player = null;

    if (server) {
        player = GM_getValue(server + "." + CACHE_LAST_PLAYER);
        if (!player) {
            player = null; 
            if (debug) { GM_log("grill_cache_getPlayer(): unable to get the last player"); }
        }
    }
    else {
        GM_log("grill_cache_getPlayer(): " + server);
    }

    return player;
}

function grill_cache_setPlayer(server, player) {
    if (server && player) {
        GM_setValue(server + "." + CACHE_LAST_PLAYER, player);
    }
    else if (debug) { GM_log("grill_cache_setPlayer(): " + server + ", " + player); }
}

// Helpers /////////////////////////////////////////////////////////////////////
function grill_cache_setPlayerProp(server, player, property, value) {
    if (server && player && property && (value || value == 0)) {
        var key = grill_cache_getPlayerKey(server, player) + property;

        GM_setValue(key, value);
    }
}

function grill_cache_getPlayerProp(server, player, property) {
    if (server && player && property) {
        return GM_getValue(grill_cache_getPlayerKey(server, player) + property);
    }

    return null;
}

function grill_cache_setColonyProp(server, player, colonyId, property, value) {
    if (server && player && colonyId && property && (value || value == 0)) {
        var key = grill_cache_getColonyKey(server, player, colonyId) + property;

        GM_setValue(key, value);
    }
    else if (debug) {
        GM_log("grill_cache_setColonyProp(): " + server + ", " + player + ", " + 
                colonyId + ", " + property + ", " + value);
    }
}

function grill_cache_getColonyProp(server, player, colonyId, property) {
    if (server && player && colonyId && property) {
        return GM_getValue(grill_cache_getColonyKey(server, player, colonyId) + property);
    }

    return null;
}

function grill_cache_getColonyPropString(server, player, colonyId, property) {
    var value = grill_cache_getColonyProp(server, player, colonyId, property);
    if (value || value == 0) {
        return value.toString();
    }

    return null;
}

function grill_cache_getColonyPropNumber(server, player, colonyId, property) {
    return Number(grill_cache_getColonyProp(server, player, colonyId, property));
}

function grill_cache_getColonyKey(server, player, colony) {
    return server + "." + player + "." + colony + ".";
}

function grill_cache_getPlayerKey(server, player) {
    return server + "." + player + ".";
}

// Colony //////////////////////////////////////////////////////////////////////

function grill_cache_getColonyLevel(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_LEVEL);
}

function grill_cache_setColonyLevel(server, player, colonyId, level) {
    return grill_cache_setColonyProp(server, player, colonyId, CACHE_LEVEL, level);
}

// Spies ///////////////////////////////////////////////////////////////////////

function grill_cache_setSpies(server, player, colonyId, spies) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_SPIES, spies);
}

function grill_cache_getSpies(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_SPIES);
}

function grill_cache_setTrainedSpies(server, player, colonyId, spies) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_TRAINED_SPIES, spies);
}

function grill_cache_getTrainedSpies(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_TRAINED_SPIES);
}

// Population //////////////////////////////////////////////////////////////////

function grill_cache_setPopulation(server, player, colonyId, population) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_POPULATION, population);
}

function grill_cache_getPopulation(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_POPULATION);
}

function grill_cache_setPopulationCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_POPULATION_CAP, cap);
}

function grill_cache_getPopulationCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_POPULATION_CAP);
}

function grill_cache_setPopulationGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_POPULATION_GROWTH, growth.toString());
    }
}

function grill_cache_getPopulationGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_POPULATION_GROWTH);
}

// Wood ////////////////////////////////////////////////////////////////////////

function grill_cache_setWood(server, player, colonyId, wood) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_WOOD, wood);
}

function grill_cache_getWood(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WOOD);
}

function grill_cache_setWoodCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_WOOD_CAP, cap);
}

function grill_cache_getWoodCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WOOD_CAP);
}

function grill_cache_setWoodGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_WOOD_GROWTH, growth.toString());
    }
}

function grill_cache_getWoodGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WOOD_GROWTH);
}

// Wine ////////////////////////////////////////////////////////////////////////

function grill_cache_setWine(server, player, colonyId, wine) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_WINE, wine);
}

function grill_cache_getWine(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WINE);
}

function grill_cache_setWineCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_WINE_CAP, cap);
}

function grill_cache_getWineCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WINE_CAP);
}

function grill_cache_setWineGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_WINE_GROWTH, growth.toString());
    }
}

function grill_cache_getWineGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_WINE_GROWTH);
}

// Marble //////////////////////////////////////////////////////////////////////

function grill_cache_setMarble(server, player, colonyId, marble) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_MARBLE, marble);
}

function grill_cache_getMarble(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_MARBLE);
}

function grill_cache_setMarbleCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_MARBLE_CAP, cap);
}

function grill_cache_getMarbleCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_MARBLE_CAP);
}

function grill_cache_setMarbleGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_MARBLE_GROWTH, growth.toString());
    }
}

function grill_cache_getMarbleGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_MARBLE_GROWTH);
}

// Crystal /////////////////////////////////////////////////////////////////////

function grill_cache_setCrystal(server, player, colonyId, crystal) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_CRYSTAL, crystal);
}

function grill_cache_getCrystal(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_CRYSTAL);
}

function grill_cache_setCrystalCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_CRYSTAL_CAP, cap);
}

function grill_cache_getCrystalCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_CRYSTAL_CAP);
}

function grill_cache_setCrystalGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_MARBLE_GROWTH, growth.toString());
    }
}

function grill_cache_getCrystalGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_CRYSTAL_GROWTH);
}

// Sulfur //////////////////////////////////////////////////////////////////////

function grill_cache_setSulfur(server, player, colonyId, sulfur) {
    if (sulfur || sulfur == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_SULFUR, sulfur);
    }
}

function grill_cache_getSulfur(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_SULFUR);
}

function grill_cache_setSulfurCap(server, player, colonyId, cap) {
    grill_cache_setColonyProp(server, player, colonyId, CACHE_SULFUR_CAP, cap);
}

function grill_cache_getSulfurCap(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_SULFUR_CAP);
}

function grill_cache_setSulfurGrowth(server, player, colonyId, growth) {
    if (growth || growth == 0) {
        grill_cache_setColonyProp(server, player, colonyId, CACHE_SULFUR_GROWTH, growth.toString());
    }
}

function grill_cache_getSulfurGrowth(server, player, colonyId) {
    return grill_cache_getColonyPropNumber(server, player, colonyId, CACHE_SULFUR_GROWTH);
}

// Buildings ///////////////////////////////////////////////////////////////////

function grill_cache_getBuilding(server, player, id, position) {
    var name = grill_cache_getBuildingName(server, player, id, position);
    var level = grill_cache_getBuildingLevel(server, player, id, position);
    var type = grill_cache_getBuildingType(server, player, id, position);
    var position = grill_cache_getBuildingPosition(server, player, id, position);

    if (name && type && (level || level == 0) && (position || position == 0)) {
        return new Grill_Building(name, type, level, position);
    }

    return null;
}

function grill_cache_getBuildings(server, player, id) {
    var buildings = new Array();
    var b;

    for (var i = 0; i < TOTAL_BUILDINGS; i++) {
        b = grill_cache_getBuilding(server, player, id, position);
        if (b) { 
            buildings[i] = b;
            buildings[b.type] = b;
        }
    }

    return buildings;
}

function grill_cache_getBuildingLevel(server, player, id, position) {
    if (position || position == 0) {
        return grill_cache_getColonyPropNumber(server, player, id, position + "." + CACHE_LEVEL);
    }

    return null;
}

function grill_cache_setBuildingLevel(server, player, id, position, level) {
    if ((position || position == 0) && level) {
        return grill_cache_setColonyProp(server, player, id, position + "." + CACHE_LEVEL, level);
    }

    return null;
}

function grill_cache_getBuildingType(server, player, id, position) {
    if (position || position == 0) {
        return grill_cache_getColonyProp(server, player, id, position + "." + CACHE_TYPE);
    }

    return null;
}

function grill_cache_setBuildingType(server, player, id, position, type) {
    if ((position || position == 0) && type) {
        return grill_cache_setColonyProp(server, player, id, position + "." + CACHE_TYPE, type);
    }

    return null;
}

function grill_cache_getBuildingName(server, player, id, position) {
    if (position || position == 0) {
        return grill_cache_getColonyProp(server, player, id, position + "." + CACHE_NAME);
    }

    return null;
}

function grill_cache_setBuildingName(server, player, id, position, name) {
    if ((position || position == 0) && name) {
        return grill_cache_setColonyProp(server, player, id, position + "." + CACHE_NAME, name);
    }

    return null;
}

/////////////////////////////////////////////////////////////////////
// Ikariam Spy Functions
/////////////////////////////////////////////////////////////////////
/**
 * Calculate the spy defense
 */
function grill_spy_getSpyDefense(levelTargetTownHall, levelTargetHideout, countTargetSpies, levelSourceHideout) {
    return 5*(countTargetSpies + 1) - 2*(levelTargetTownHall - levelTargetHideout + levelSourceHideout);
}

/**
 * Find the minimum level where the spy defense cap dips below 95%
 */
function grill_spy_getEnemyCap(levelTargetTownHall, levelTargetHideout, countTargetSpies) {
    return (5*countTargetSpies - 2*levelTargetTownHall + 2*levelTargetHideout - 90)/2;
}

////////////////////////////////////////////////////////////////////////////////
// HTTP Functions
////////////////////////////////////////////////////////////////////////////////
function grill_http_newPost() {
    return {method:'POST',
            url:'http://' + grill_getServer() + '/index.php',
            data:'',
            headers: {
//              'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
                'Content-type': 'application/x-www-form-urlencoded',
                'Accept': 'application/xml,text/xml',
                'Referer': top.location
            },
            onload:null};
}

function grill_http_post(data, callBack) {
    if (data && callBack) {
        var post = grill_http_newPost();
        post["data"] = data;
        post["onload"] = callBack;

        GM_xmlhttpRequest(post);
    }
}

function grill_http_getScore(player, score, callBack) {
    var data = "view=highscore&highscoreType=" + score + "&searchUser=" + player;
    grill_http_post(data, callBack);
}

////////////////////////////////////////////////////////////////////////////////
// URL Functions
////////////////////////////////////////////////////////////////////////////////
function grill_url_getVariable(name, url) {
    if (url && name) {
        return grill_url_getVariables(url)[name];
    }

    return null;
}

function grill_url_getVariables(url) {
    var a = new Array();

    if (url) {
        var split = url.toString().split("?");
        if (split && split.length > 1) {
            split = split[1].split("&");
            if (split) {
                var i = 0;
                for each (var string in split) {
                    if (string.split) {
                        split = string.split("=");
                        if (split) {
                            a[i] = split[1]; //Arrays are broken in Firefox, gotta do this first
                            a[split[0]] = split[1];
                            i++;
                        }
                    }
                }
            }
        }
    }

    return a;
}

////////////////////////////////////////////////////////////////////////////////
// General Functions
////////////////////////////////////////////////////////////////////////////////
/**
 * Trims any white space
 */
function grill_trim(string) {
    if (string) {
        try {
            return string.toString().replace(/^\s+|\s+$/g, '');
        }
        catch (e) { GM_log(e); }
    }

    return null;
}

////////////////////////////////////////////////////////////////////////////////
// Xpath Query Functions
////////////////////////////////////////////////////////////////////////////////
function grill_xpath_getUnorderedNodeIterator(xpath) {
    var node = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
    if (node) {
        return node;
    }

    return null;
}

function grill_xpath_getUnorderedNodeSnapshot(xpath) {
    var node = document.evaluate(xpath, document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    if (node) {
        return node;
    }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node
 */
function grill_xpath_getFirstNode(xpath) {
    var node = document.evaluate(xpath, document, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
    if (node) {
        return node.singleNodeValue;
    }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as a string
 */
function grill_xpath_getFirstNodesLastString(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
        
        if (node && node.lastChild) {
            return grill_trim("" + node.lastChild.nodeValue);
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

function grill_xpath_getFirstNodesFirstString(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
        
        if (node && node.lastChild) {
            return grill_trim("" + node.firstChild.nodeValue);
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as an int
 */
function grill_xpath_getFirstNodesLastInt(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
    
        if (node && node.lastChild && node.lastChild.nodeValue) {
            return parseInt(node.lastChild.nodeValue.replace(',', '')); //Remove any commas and return an int
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as an int
 */
function grill_xpath_getFirstNodesLastFloat(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
    
        if (node && node.lastChild) {
            return parseFloat(node.lastChild.nodeValue); //return a float
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as a string
 */
function grill_xpath_getFirstNodesFirstString(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
        
        if (node && node.lastChild) {
            return grill_trim("" + node.firstChild.nodeValue);
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as an int
 */
function grill_xpath_getFirstNodesFirstInt(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
    
        if (node && node.lastChild) {
            return parseInt(node.firstChild.nodeValue); //return an int
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as an int
 */
function grill_xpath_getFirstNodesFirstFloat(xpath, error) {
    if (xpath) {
        var node = grill_xpath_getFirstNode(xpath);
    
        if (node && node.lastChild) {
            return parseFloat(node.firstChild.nodeValue); //return a float
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

/**
 * Uses an xpath query to find the first matching Node returned as an int
 */
function grill_xpath_getFirstNodesUrlVariable(xpath, variable, error) {
    if (xpath && variable) {
        var node = grill_xpath_getFirstNode(xpath);
    
        if (node) { 
            return grill_url_getVariable(variable, node); 
        }
    }

    if (debug && error) { GM_log(error); }

    return null;
}

////////////////////////////////////////////////////////////////////////////////
// DOM Functions
////////////////////////////////////////////////////////////////////////////////
function grill_dom_getElementValueById(doc, id, defaultValue) {
    var value = null;
    var element = document.getElementById(id);

    if (element && element.firstChild) {
        value = element.firstChild.nodeValue;
    }
    
    if (!value && defaultValue) { value = defaultValue; }

    return value;
}

function grill_dom_getElementNumberById(doc, id, defaultValue) {
    var value = null;
    var result = grill_dom_getElementValueById(doc, id, defaultValue);
    if (result) {
        value = Number(result.replace(',', ''));
    }

    return value;
}

/**
 * Insert a div after a particular ID
 */
function grill_dom_insertAfter(element, before) {
    if (element && before) {
        before.parentNode.insertBefore(element, before.nextSibling);
        return true;
    }
    else {
        if (debug) { GM_log("grill_dom_insertElementAfterId() Element: " + element + ", ID: " + id); }
    }

    return false; //Didn't insert
}

/**
 * Insert a div after a particular ID
 */
function grill_dom_insertAfterId(element, id) {
    if (element && id) {
        var before = document.getElementById(id);
        if (before) {
            return grill_dom_insertAfter(element, before);
        }
        else {
            if (debug) { GM_log("Unable to find element with id '" + id + "'"); }
        }
    }
    else {
        if (debug) { GM_log("grill_dom_insertElementAfterId() Element: " + element + ", ID: " + id); }
    }

    return false; //Didn't insert
}

function grill_dom_updateId(id, text) {
    var element = document.getElementById(id);
    if (element) {
        element.innerHTML = text;
    }
    else { GM_log("Could not find element: " + id); }
}